<html>
 <head>
  <meta charset="UTF-8">
 </head>
 <body>
  <h1 data-lake-id="LRqYf" id="LRqYf"><span data-lake-id="u58954af8" id="u58954af8">典型回答</span></h1>
  <p data-lake-id="ue833a7b2" id="ue833a7b2"><br></p>
  <p data-lake-id="u625ba2b9" id="u625ba2b9"><strong><span data-lake-id="u2b4bdd4c" id="u2b4bdd4c">RedLock是Redis的作者提出的一个多节点分布式锁算法，旨在解决使用单节点Redis分布式锁可能存在的单点故障问题。</span></strong><span data-lake-id="uda06251d" id="uda06251d">（</span><a href="https://redis.io/docs/manual/patterns/distributed-locks/" target="_blank" data-lake-id="u70218d0a" id="u70218d0a"><span data-lake-id="u2ebe37e4" id="u2ebe37e4">https://redis.io/docs/manual/patterns/distributed-locks/</span></a><span data-lake-id="u9c0db1ca" id="u9c0db1ca"> ）</span></p>
  <p data-lake-id="ue9c0d786" id="ue9c0d786"><span data-lake-id="uee57a489" id="uee57a489">​</span><br></p>
  <p data-lake-id="ude65ab6a" id="ude65ab6a"><span data-lake-id="u6cfd95d6" id="u6cfd95d6">Redis的单点故障问题：</span></p>
  <p data-lake-id="u23e1e05a" id="u23e1e05a"><span data-lake-id="ue104d779" id="ue104d779">1、在使用单节点Redis实现分布式锁时，如果这个Redis实例挂掉，那么所有使用这个实例的客户端都会出现无法获取锁的情况。</span></p>
  <p data-lake-id="uf516b765" id="uf516b765"><span data-lake-id="u1b7ce6a9" id="u1b7ce6a9">2、当使用集群模式部署的时候，如果master一个客户端在master节点加锁成功了，然后没来得及同步数据到其他节点上，他就挂了， 那么这时候如果选出一个新的节点，再有客户端来加锁的时候，就也能加锁成功，因为数据没来得及同步，新的master会认为这个key是不存在的。</span></p>
  <p data-lake-id="u45367993" id="u45367993"><span data-lake-id="u2d3ce6dc" id="u2d3ce6dc">​</span><br></p>
  <p data-lake-id="u0333d77c" id="u0333d77c"><img src="https://cdn.nlark.com/yuque/0/2024/png/5378072/1705138438131-c160ac35-1707-42c4-868b-ac2313f53697.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_25%2Ctext_SmF2YSA4IEd1IFA%3D%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10"></p>
  <p data-lake-id="u2b58ae1a" id="u2b58ae1a"><span data-lake-id="u65c74e52" id="u65c74e52">如上图，就是我们前面说的第二个问题。</span></p>
  <p data-lake-id="u8b4a2b16" id="u8b4a2b16"><span data-lake-id="ud07b820f" id="ud07b820f">​</span><br></p>
  <p data-lake-id="u4a292d16" id="u4a292d16"><span data-lake-id="udaf1b70d" id="udaf1b70d">RedLock通过使用多个Redis节点，来提供一个更加健壮的分布式锁解决方案，能够在某些Redis节点故障的情况下，仍然能够保证分布式锁的可用性。</span></p>
  <p data-lake-id="u60ac49ea" id="u60ac49ea"><span data-lake-id="u7876ccf4" id="u7876ccf4">​</span><br></p>
  <p data-lake-id="u23b419a7" id="u23b419a7"><strong><span data-lake-id="ub584944a" id="ub584944a">RedLock是通过引入多个Redis节点来解决单点故障的问题。</span></strong></p>
  <p data-lake-id="ua0e8c3d4" id="ua0e8c3d4"><span data-lake-id="u07530e8e" id="u07530e8e">​</span><br></p>
  <p data-lake-id="u353ce5ab" id="u353ce5ab"><span data-lake-id="ude001b51" id="ude001b51">在进行加锁操作时，RedLock会向每个Redis节点发送相同的命令请求，每个节点都会去竞争锁，如果至少在大多数节点上成功获取了锁，那么就认为加锁成功。反之，如果大多数节点上没有成功获取锁，则加锁失败。这样就可以避免因为某个Redis节点故障导致加锁失败的情况发生。</span></p>
  <p data-lake-id="ub2c886dd" id="ub2c886dd"><span data-lake-id="u00c5d2a8" id="u00c5d2a8">​</span><br></p>
  <p data-lake-id="ub1b7d25c" id="ub1b7d25c"><img src="https://cdn.nlark.com/yuque/0/2024/png/5378072/1705138495619-e0c38366-d39e-40a4-ac57-7782ed3b631b.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_25%2Ctext_SmF2YSA4IEd1IFA%3D%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10"></p>
  <p data-lake-id="uc8663460" id="uc8663460"><br></p>
  <p data-lake-id="uabb27d66" id="uabb27d66"><span data-lake-id="ued59d1c7" id="ued59d1c7">在redis集群中有3个节点的情况下：</span></p>
  <p data-lake-id="u3a5348f0" id="u3a5348f0"><br></p>
  <p data-lake-id="ub355b2a5" id="ub355b2a5"><span data-lake-id="uf43a1eec" id="uf43a1eec">1、客户端想要获取锁时，会生成一个全局唯一的ID（官方文档建议使用系统时间来生成这个ID）</span></p>
  <p data-lake-id="u4a62b932" id="u4a62b932"><span data-lake-id="u572569ff" id="u572569ff">2、客户端尝试使用这个ID获取所有redis节点的同意，这一步通过使用SETNX命令实现。</span></p>
  <p data-lake-id="u1eed1311" id="u1eed1311"><span data-lake-id="u85ad1dac" id="u85ad1dac">3、如果有2个以上的节点同意，那么锁就被成功设置了。</span></p>
  <p data-lake-id="u2d5d19f3" id="u2d5d19f3"><span data-lake-id="u19f4dec8" id="u19f4dec8">4、获取锁之后，用户可以执行想要的操作。</span></p>
  <p data-lake-id="ud923c16d" id="ud923c16d"><span data-lake-id="uf1b81ad0" id="uf1b81ad0">5、最后，不想用这把锁的时候，再尝试依次解锁，无论锁是否成功获取。</span></p>
  <p data-lake-id="u899d6a81" id="u899d6a81"><br></p>
  <p data-lake-id="ub70c02f2" id="ub70c02f2"><span data-lake-id="u35cf1e01" id="u35cf1e01">这样，当超过半数以上的节点都写入成功之后，即使master挂了，新选出来的master也能保证刚刚的那个key一定存在（否则这个节点就不会被选为master）。</span></p>
  <p data-lake-id="u87759efc" id="u87759efc"><span data-lake-id="u9cae5635" id="u9cae5635">​</span><br></p>
  <p data-lake-id="u9c02e11a" id="u9c02e11a"><span data-lake-id="u14f78254" id="u14f78254">需要注意的是，RedLock并不能完全解决分布式锁的问题。例如，在脑裂的情况下，RedLock可能会产生两个客户端同时持有锁的情况。</span></p>
  <p data-lake-id="u05b3a72e" id="u05b3a72e"><span data-lake-id="ue3f5f5c0" id="ue3f5f5c0">​</span><br></p>
  <h1 data-lake-id="d1ilh" id="d1ilh"><span data-lake-id="u94e16d0f" id="u94e16d0f">扩展知识</span></h1>
  <p data-lake-id="u9182df6c" id="u9182df6c"><br></p>
  <h2 data-lake-id="QIzyw" id="QIzyw"><span data-lake-id="u081a397f" id="u081a397f">如何使用</span></h2>
  <p data-lake-id="u4dafc6ae" id="u4dafc6ae"><br></p>
  <p data-lake-id="uec527bef" id="uec527bef"><span data-lake-id="udb998dd8" id="udb998dd8">了解了RedLock的机制之后，我们在Java中如何使用呢？可以直接使用RedissonRedLock，他是支持RedLock算法的：</span></p>
  <p data-lake-id="ue2e9f32d" id="ue2e9f32d"><span data-lake-id="uf12090a7" id="uf12090a7">​</span><br></p>
  <pre lang="java"><code>
Config config1 = new Config();
config1.useSingleServer()
       .setAddress("redis://127.0.0.1:6379");

Config config2 = new Config();
config2.useSingleServer()
       .setAddress("redis://127.0.0.1:6380");

Config config3 = new Config();
config3.useSingleServer()
       .setAddress("redis://127.0.0.1:6381");

RedissonClient redissonClient1 = Redisson.create(config1);
RedissonClient redissonClient2 = Redisson.create(config2);
RedissonClient redissonClient3 = Redisson.create(config3);

RLock lock1 = redissonClient1.getLock("lockKey");
RLock lock2 = redissonClient2.getLock("lockKey");
RLock lock3 = redissonClient3.getLock("lockKey");

RedissonRedLock redLock = new RedissonRedLock(lock1, lock2, lock3);

boolean lockResult = redLock.tryLock(waitTime, leaseTime, TimeUnit.MILLISECONDS);

// 业务逻辑

if (lockResult) {
    try {
        // 业务逻辑
    } finally {
        redLock.unlock();
    }
} else {
    // 获取锁失败的处理逻辑
}

redissonClient1.shutdown();
redissonClient2.shutdown();
redissonClient3.shutdown();

</code></pre>
  <p data-lake-id="u5340a718" id="u5340a718"><span data-lake-id="u919194c7" id="u919194c7">​</span><br></p>
  <p data-lake-id="u601f22fb" id="u601f22fb"><span data-lake-id="uf920164e" id="uf920164e">除了Redisson，还有其他的一些工具可以用于实现RedLock，比如Java的</span><strong><span data-lake-id="ue7b7d5a1" id="ue7b7d5a1">Redlock-java</span></strong><span data-lake-id="u05f3146a" id="u05f3146a">库、Go的Redsync库等。这些工具的使用方式类似，都是创建多个Redis实例，然后使用RedLock算法获取分布式锁。</span></p>
  <p data-lake-id="u03136ce6" id="u03136ce6"><br></p>
  <h2 data-lake-id="C1KJZ" id="C1KJZ"><span data-lake-id="u50043422" id="u50043422">分歧</span></h2>
  <p data-lake-id="uc2fbb0da" id="uc2fbb0da"><br></p>
  <p data-lake-id="uc9bc8ecf" id="uc9bc8ecf"><span data-lake-id="u4b94381d" id="u4b94381d">关于RedLock，两位大佬曾经展开过激烈的讨论，感兴趣可以了解一下。</span></p>
  <p data-lake-id="u27cb02f7" id="u27cb02f7"><span data-lake-id="u60f4e5f2" id="u60f4e5f2">​</span><br></p>
  <p data-lake-id="u9a544fde" id="u9a544fde"><a href="https://martin.kleppmann.com/2016/02/08/how-to-do-distributed-locking.html" target="_blank" data-lake-id="u27be272b" id="u27be272b"><span data-lake-id="ubaf430a5" id="ubaf430a5">https://martin.kleppmann.com/2016/02/08/how-to-do-distributed-locking.html</span></a></p>
  <p data-lake-id="ue66a659a" id="ue66a659a"><span data-lake-id="u765e3240" id="u765e3240">​</span><br></p>
  <p data-lake-id="uc564ee85" id="uc564ee85"><a href="http://antirez.com/news/101" target="_blank" data-lake-id="u15ceea47" id="u15ceea47"><span data-lake-id="ufe94b7fa" id="ufe94b7fa">http://antirez.com/news/101</span></a></p>
  <p data-lake-id="ud6bdca72" id="ud6bdca72"><br></p>
  <p data-lake-id="uf729be42" id="uf729be42"><span data-lake-id="u2297eff6" id="u2297eff6">Martin 是著名的分布式系统专家，在他的博客上发表了一篇文章(</span><a href="https://martin.kleppmann.com/2016/02/08/how-to-do-distributed-locking.html" target="_blank" data-lake-id="u3249b276" id="u3249b276"><span data-lake-id="ue1fae87d" id="ue1fae87d">https://martin.kleppmann.com/2016/02/08/how-to-do-distributed-locking.html</span></a><span data-lake-id="u80554de5" id="u80554de5"> )。在这篇文章中，他认为使用 RedLock 实现分布式锁是有问题的。他的观点是</span><strong><span data-lake-id="u024ddf47" id="u024ddf47">：RedLock 存在着很多潜在的问题，比如网络延迟、时钟漂移等问题。</span></strong><span data-lake-id="u7e9f6548" id="u7e9f6548">他认为使用 Redis 的 SET NX EX 命令来实现分布式锁，他认为这种方案是更加简单、可靠、安全的。</span></p>
  <p data-lake-id="uf95404d6" id="uf95404d6"><span data-lake-id="u45cf00bc" id="u45cf00bc">​</span><br></p>
  <ol list="u7abe8446">
   <li fid="u806dceb5" data-lake-id="u3a676e6f" id="u3a676e6f"><span data-lake-id="u93cc580a" id="u93cc580a">网络分区：在网络分区的情况下，不同的节点可能会获取到相同的锁，这会导致分布式系统的不一致性问题。</span></li>
   <li fid="u806dceb5" data-lake-id="u96a7a82a" id="u96a7a82a"><span data-lake-id="u32ac34f6" id="u32ac34f6">时间漂移：由于不同的机器之间的时间可能存在微小的漂移，这会导致锁的失效时间不一致，也会导致分布式系统的不一致性问题。</span></li>
   <li fid="u806dceb5" data-lake-id="ucb7a5b00" id="ucb7a5b00"><span data-lake-id="ub571acd5" id="ub571acd5">Redis 的主从复制：在 Redis 主从复制的情况下，如果 Redis 的主节点出现故障，需要选举新的主节点。这个过程中可能会导致锁的丢失，同样会导致分布式系统的不一致性问题。</span></li>
  </ol>
  <p data-lake-id="uf1e7fc48" id="uf1e7fc48"><span data-lake-id="ue9b369f9" id="ue9b369f9">​</span><br></p>
  <p data-lake-id="ueb9dadd6" id="ueb9dadd6"><span data-lake-id="u954fd8c4" id="u954fd8c4">Antirez 是 Redis 的作者，，他在自己的博客上发表了一篇文章(</span><a href="http://antirez.com/news/101" target="_blank" data-lake-id="udf992872" id="udf992872"><span data-lake-id="u4bff11c6" id="u4bff11c6">http://antirez.com/news/101</span></a><span data-lake-id="u20cf762c" id="u20cf762c"> )，对 Martin 的观点提出了不同看法。他认为，虽然 RedLock 算法可能存在一些问题，但是这种算法本质上是正确的，可以保证分布式锁的正确性。他认为使用 Redis 实现分布式锁也存在着很多问题，比如 Redis 服务器崩溃、网络分区等问题，而 RedLock 算法可以一定程度上解决这些问题。</span></p>
  <p data-lake-id="ub61fe749" id="ub61fe749"><span data-lake-id="ubd3037b0" id="ubd3037b0">​</span><br></p>
  <ol list="u1f0e0ce4">
   <li fid="u6e7b7f34" data-lake-id="ucbcc69c3" id="ucbcc69c3"><span data-lake-id="uc17f0089" id="uc17f0089">网络分区：在网络分区的情况下，RedLock 仍然可以提供足够的可靠性。虽然会存在节点获取到相同锁的情况，但这种情况只会发生在网络分区发生时，且只会发生在一小部分节点上。而在网络分区恢复后，RedLock 会自动解锁。</span></li>
   <li fid="u6e7b7f34" data-lake-id="u46194d6e" id="u46194d6e"><span data-lake-id="ua6de3120" id="ua6de3120">时间漂移：RedLock 可以使用 NTP 等工具来同步不同机器之间的时间，从而避免时间漂移导致的问题。</span></li>
   <li fid="u6e7b7f34" data-lake-id="ufac06bda" id="ufac06bda"><span data-lake-id="u05d2907e" id="u05d2907e">Redis 的主从复制：虽然 Redis 的主从复制可能导致锁的丢失，但这种情况非常罕见，并且可以通过多种方式来避免，例如使用 Redis Cluster。</span></li>
  </ol>
 </body>
</html>